package org.tabularium.util;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.sql.Time;
import java.util.Vector;

/**
 * Date and time helper functions.
 * 
 * See the package 
 */
public final class DateTime {
	private static final char DATE_SEPARATORS[] = { '.', '-', '/' };

	/**
	 * Parse date in one of the formats: "dd.mm.yyyy", "dd.mm.yy", "yyyy.mm.dd",
	 * "dd-mm-yyyy", "yy-mm-dd", "yyyy-mm-dd", "mm/dd/yyyy", "mm/dd/yy" with one
	 * or two digits day and month and optional time in one of the formats:
	 * "hh:mm", "hh:mm:ss" with optional am/pm suffix
	 * 
	 * @param string
	 *            String containing date representation
	 * @return Date object or null if date cannot be parsed
	 */
	public static java.util.Date parseDateTime(String string) {
		java.util.GregorianCalendar gc = parseDateTimeToCalendar(string);
		return gc != null ? gc.getTime() : null;
	}

	/**
	 * Parse date in one of the formats: "dd.mm.yyyy", "dd.mm.yy", "yyyy.mm.dd",
	 * "dd-mm-yyyy", "yy-mm-dd", "yyyy-mm-dd", "mm/dd/yyyy", "mm/dd/yy" with one
	 * or two digits day and month.
	 * 
	 * @param string
	 *            String containing date representation
	 * @return Date object or null if date cannot be parsed
	 */
	public static java.util.Date parseDate(String string) {
		java.util.GregorianCalendar gc = parseDateToCalendar(string);
		return gc != null ? gc.getTime() : null;
	}
	/**
	 * Parse date in one of the formats: "dd.mm.yyyy", "dd.mm.yy", "yyyy.mm.dd",
	 * "dd-mm-yyyy", "yy-mm-dd", "yyyy-mm-dd", "mm/dd/yyyy", "mm/dd/yy" with one
	 * or two digits day and month and optional time in one of the formats:
	 * "hh:mm", "hh:mm:ss" with optional am/pm suffix
	 * 
	 * @param string
	 *            String containing date representation
	 * @return GregorianCalendar object or null if date cannot be parsed
	 */
	public static java.util.GregorianCalendar parseDateTimeToCalendar(String string) {
		if (string == null || string.length() == 0) {
			return null;
		}

		string = string.trim();
		String dateStr = null, timeStr = null;
		int spacePos = string.indexOf(' ');
		if (spacePos > 0) {
			dateStr = string.substring(0, spacePos);
			timeStr = string.substring(spacePos + 1, string.length());
		} else {
			dateStr = string;
		}

		java.util.GregorianCalendar gc = null;
		try {
			// parse date part
			// select date format
			// find separator
			char separator = ' ';
			for (int i = 0; i < DATE_SEPARATORS.length; i++) {
				if (dateStr.indexOf(DATE_SEPARATORS[i]) > 0) {
					separator = DATE_SEPARATORS[i];
				}
			}
			if (separator == ' ') {
				return null;
			}
			// get date parts
			java.util.StringTokenizer st = new java.util.StringTokenizer(
					dateStr, "" + separator);
			if (st.countTokens() != 3) {
				return null;
			}
			int part1 = Integer.parseInt(st.nextToken());
			int part2 = Integer.parseInt(st.nextToken());
			int part3 = Integer.parseInt(st.nextToken());
			int day = 0, month = 0, year = 0;
			switch (separator) {
			case '.':
				day = part1;
				month = part2;
				year = part3;
				break;
			case '/':
				month = part1;
				day = part2;
				year = part3;
				break;
			default:
				year = part3 > 31 ? part3 : part1;
				month = part2;
				day = part3 > 31 ? part1 : part3;
				break;
			}
			if (year < 100) {
				year += 2000;
			}
			month--; // months are 0-based

			// parse time part
			int hour = 0, minute = 0, second = 0;
			if (timeStr != null) {
				timeStr = timeStr.toLowerCase();
				if (timeStr.endsWith("pm")) {
					hour += 12;
					timeStr = timeStr.substring(0, timeStr.length() - 2).trim();
				}
				if (timeStr.endsWith("am")) {
					timeStr = timeStr.substring(0, timeStr.length() - 2).trim();
				}
				st = new java.util.StringTokenizer(timeStr, ":");
				int tokens = st.countTokens();
				if (tokens < 2 || tokens > 3) {
					return null;
				}
				hour += Integer.parseInt(st.nextToken());
				minute = Integer.parseInt(st.nextToken());
				if (tokens == 3) {
					second = Integer.parseInt(st.nextToken());
				}
			}

			// get Date object
			gc = new java.util.GregorianCalendar(year, month, day, hour,
					minute, second);
		} catch (Exception ex) {
			/* ignore */
		}
		return gc;
	}

	/**
	 * Parse date in one of the formats: "dd.mm.yyyy", "dd.mm.yy", "yyyy.mm.dd",
	 * "dd-mm-yyyy", "yy-mm-dd", "yyyy-mm-dd", "mm/dd/yyyy", "mm/dd/yy" with one
	 * or two digits day and month and optional time in one of the formats:
	 * "hh:mm", "hh:mm:ss" with optional am/pm suffix
	 * 
	 * @param string
	 *            String containing date representation
	 * @return GregorianCalendar object or null if date cannot be parsed
	 */
	public static java.util.GregorianCalendar parseDateToCalendar(String dateStr) {
		if (dateStr == null || dateStr.length() == 0) {
			return null;
		}

		dateStr = dateStr.trim();

		java.util.GregorianCalendar gc = null;
		try {
			// parse date part
			// select date format
			// find separator
			char separator = ' ';
			for (int i = 0; i < DATE_SEPARATORS.length; i++) {
				if (dateStr.indexOf(DATE_SEPARATORS[i]) > 0) {
					separator = DATE_SEPARATORS[i];
				}
			}
			if (separator == ' ') {
				return null;
			}
			// get date parts
			java.util.StringTokenizer st = new java.util.StringTokenizer(
					dateStr, "" + separator);
			if (st.countTokens() != 3) {
				return null;
			}
			int part1 = Integer.parseInt(st.nextToken());
			int part2 = Integer.parseInt(st.nextToken());
			int part3 = Integer.parseInt(st.nextToken());
			int day = 0, month = 0, year = 0;
			switch (separator) {
			case '.':
				day = part1;
				month = part2;
				year = part3;
				break;
			case '/':
				month = part1;
				day = part2;
				year = part3;
				break;
			default:
				year = part3 > 31 ? part3 : part1;
				month = part2;
				day = part3 > 31 ? part1 : part3;
				break;
			}
			if (year < 100) {
				year += 2000;
			}
			month--; // months are 0-based

			// get Date object
			gc = new java.util.GregorianCalendar(year, month, day);
		} catch (Exception ex) {
			/* ignore */
		}
		return gc;
	}

	/**
	 * Parse date in one of the formats: "dd.mm.yyyy", "dd.mm.yy", "yyyy.mm.dd",
	 * "dd-mm-yyyy", "yy-mm-dd", "yyyy-mm-dd", "mm/dd/yyyy", "mm/dd/yy" with one
	 * or two digits day and month and optional time in one of the formats:
	 * "hh:mm", "hh:mm:ss" with optional am/pm suffix
	 * 
	 * @param string
	 *            String containing date representation
	 * @return GregorianCalendar object or null if date cannot be parsed
	 */
	public static Time parseTime(String s) {
		if (s == null || s.length() == 0) {
			return null;
		}
		java.util.StringTokenizer st;
		java.sql.Time t = null;
		try {

			// parse time part
			int hour = 0, minute = 0, second = 0;
			if (s != null) {
				s = s.toLowerCase();
				if (s.endsWith("pm")) {
					hour += 12;
					s = s.substring(0, s.length() - 2).trim();
				}
				if (s.endsWith("am")) {
					s = s.substring(0, s.length() - 2).trim();
				}
				st = new java.util.StringTokenizer(s, ":");
				int tokens = st.countTokens();
				if (tokens < 2 || tokens > 3) {
					return null;
				}
				hour += Integer.parseInt(st.nextToken());
				minute = Integer.parseInt(st.nextToken());
				if (tokens == 3) {
					second = Integer.parseInt(st.nextToken());
				}
			}

			t = new java.sql.Time(hour*3600000+minute*60000+second*1000);
		} catch (Exception ex) {
			/* ignore */
		}
		return t;
	}

}